This notebook is Tom’s analysis from raw data

source("../CamProt_R/Utility.R")
library(tidyverse)
── Attaching packages ────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ tibble  2.0.0     ✔ purrr   0.2.5
✔ readr   1.3.1     ✔ stringr 1.3.1
✔ tibble  2.0.0     ✔ forcats 0.3.0
── Conflicts ───────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ MSnbase::combine()       masks Biobase::combine(), BiocGenerics::combine(), dplyr::combine()
✖ S4Vectors::expand()      masks tidyr::expand()
✖ dplyr::filter()          masks stats::filter()
✖ S4Vectors::first()       masks dplyr::first()
✖ dplyr::lag()             masks stats::lag()
✖ BiocGenerics::Position() masks ggplot2::Position(), base::Position()
✖ purrr::reduce()          masks MSnbase::reduce()
✖ S4Vectors::rename()      masks dplyr::rename()
infile <- "Input/OOPS_qLOPIT_LabelFree_PeptideGroups_parsed.txt"
samples_inf <- "Input/samples.tsv"
peptides_df <- parse_features(infile, silac=FALSE, TMT=FALSE, level="peptide",
                              filter_crap=TRUE, protein_col='Master.Protein.Accessions')
Tally of features at each stage:
29067   All features
These features are associated with 2731 master proteins
27882   Excluding features without a master protein
These features are associated with 2730 master proteins
26302   Excluding features without a unique master protein
These features are associated with 2405 master proteins
26139   Excluding features matching a cRAP protein
These features are associated with 2396 master proteins
Identified an additional 0 proteins as 'cRAP associated'
colnames(peptides_df)
 [1] "Checked"                           "Confidence"                        "Sequence"                         
 [4] "Modifications"                     "Qvality.PEP"                       "Qvality.q.value"                  
 [7] "Number.of.Protein.Groups"          "Number.of.Proteins"                "Number.of.PSMs"                   
[10] "Master.Protein.Accessions"         "Number.of.Missed.Cleavages"        "Theo.MHplus.in.Da"                
[13] "Found.in.Sample.in.S1.F1.Sample"   "Found.in.Sample.in.S2.F2.Sample"   "Found.in.Sample.in.S3.F3.Sample"  
[16] "Found.in.Sample.in.S4.F4.Sample"   "Found.in.Sample.in.S6.F6.Sample"   "Found.in.Sample.in.S5.F5.Sample"  
[19] "Found.in.Sample.in.S7.F7.Sample"   "Found.in.Sample.in.S8.F8.Sample"   "Found.in.Sample.in.S9.F9.Sample"  
[22] "Found.in.Sample.in.S10.F10.Sample" "Found.in.Sample.in.S11.F11.Sample" "Found.in.Sample.in.S12.F12.Sample"
[25] "Found.in.Sample.in.S13.F13.Sample" "Found.in.Sample.in.S14.F14.Sample" "Found.in.Sample.in.S15.F15.Sample"
[28] "Found.in.Sample.in.S16.F16.Sample" "Found.in.Sample.in.S17.F17.Sample" "Found.in.Sample.in.S18.F18.Sample"
[31] "Found.in.Sample.in.S19.F19.Sample" "Found.in.Sample.in.S20.F20.Sample" "Area.F1.Sample"                   
[34] "Area.F2.Sample"                    "Area.F3.Sample"                    "Area.F4.Sample"                   
[37] "Area.F6.Sample"                    "Area.F5.Sample"                    "Area.F7.Sample"                   
[40] "Area.F8.Sample"                    "Area.F9.Sample"                    "Area.F10.Sample"                  
[43] "Area.F11.Sample"                   "Area.F12.Sample"                   "Area.F13.Sample"                  
[46] "Area.F14.Sample"                   "Area.F15.Sample"                   "Area.F16.Sample"                  
[49] "Area.F17.Sample"                   "Area.F18.Sample"                   "Area.F19.Sample"                  
[52] "Area.F20.Sample"                   "XCorr.Sequest.HT"                  "Confidence.Sequest.HT"            
[55] "Percolator.q.Value.Sequest.HT"     "Percolator.PEP.Sequest.HT"         "master_protein"                   
[58] "protein_length"                    "protein_description"               "peptide_start"                    
[61] "peptide_end"                       "crap_protein"                      "associated_crap_protein"          
[64] "unique"                            "filename"                         
peptides_quant <- makeMSNSet(obj=peptides_df, samples_inf=samples_inf, ab_col_ix=2, level="peptide", quant_name="Area")

tallies for missing data (# samples with missing)
   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20 
  14   32   87   84   88  120  135  144  220  275  322  380  517  666  936 1234 1696 2528 4033 8276 4352 

4352 peptides do not have any quantification values

So lots and lots of missing values! most peptides are quantified in only a very minor subset of fractions (<5/20). This is no suprise since we’re dealing with LFQ and subcellular fractionation here.

plotMissing(peptides_quant)
Out of 21787 total features, 21773 (99.936%) have missing values

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19 
  32   87   84   88  120  135  144  220  275  322  380  517  666  936 1234 1696 2528 4033 8276 
And 21741 features have more than one missing value

What about if we subset to GO annotated RBPs?

human_go <- readRDS("./Input/h_sapiens_go_full.rds")
GO_RBPs <- human_go %>% filter(GO.ID=="GO:0003723") %>% pull(UNIPROTKB)
peptides_quant[fData(peptides_quant)$master_protein %in% GO_RBPs,] %>% plotMissing()

Ok, so this doesn’t make any difference, 99.9% have a missing value!

Let’s aggregate to the unique peptide sequence level (ignorning variable modifications) and then see whether that reduces our problem

peptides_no_mod_quant <- agg_to_peptides(peptides_quant)
Your data contains missing values. Please read the relevant section in the combineFeatures manual page for
details the effects of missing values on data aggregation.

Nope, not really! We still have 19304 features (previously 21688) and 99.9% have missing values!

plotMissing(peptides_no_mod_quant)
Out of 19403 total features, 19389 (99.928%) have missing values

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19 
  33  100   85   84  129  144  147  220  295  338  385  525  682  911 1202 1647 2397 3582 6483 
And 19356 features have more than one missing value

OK, so we’re going to have to impute. Note that the missing valuesa are particularly present in the first 2 fractions and across fractions 3-7. After that we see fewer missing values. Also, remember that we have yet to identify any definite set of RNAs from the initial fractions (1-7ish). For this reason, we’ll remove these first 5 fractions for now and leave fraction 6 & 7 as these are useful to separate light membranes

plot(colSums(is.na(exprs(peptides_no_mod_quant))))

#peptides_no_mod_quant_no_lm <- peptides_no_mod_quant[,8:20]
peptides_no_mod_quant_no_lm <- peptides_no_mod_quant[,6:20]
plotMissing(peptides_no_mod_quant_no_lm)
Out of 19403 total features, 19258 (99.253%) have missing values

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15 
 121  188  233  267  291  332  478  594  816 1063 1562 2381 3561 6592  779 
And 19137 features have more than one missing value
png("./Output/plots/missing_values_peptide.png")
plotMissing(peptides_no_mod_quant_no_lm)
Out of 19403 total features, 19258 (99.253%) have missing values

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15 
 121  188  233  267  291  332  478  594  816 1063 1562 2381 3561 6592  779 
And 19137 features have more than one missing value
dev.off()
png 
  2 

Ok, so now we’re down to just 99.1% missing :p but at least we have some more peptides now with relatively few (<=4 missing values)

If we focus on those peptides with 4-9 missing values, we can see that many are missing in a block of sequential fractions. Arguably, these are true missing values, e.g not at random.

missing_n <- rowSums(is.na(exprs(peptides_no_mod_quant_no_lm)))
peptides_no_mod_quant_no_lm[(missing_n>=4 & missing_n<=9),] %>% plotMissing()
Out of 2778 total features, 2778 (100%) have missing values

  4   5   6   7   8   9 
267 291 332 478 594 816 
And 2778 features have more than one missing value

We can identify the missing values which are in a sequential block of >=5 fractions in a row and replace these with zero

First, let’s make a function to identify rows of values where the missing values are not random, e.g 4 or more in a row

test_values_1 <- c(1,1,NA,1,NA,NA,1,1,1,1)
test_values_2 <- c(1,1,NA,NA,NA,NA,NA,1,1,1)
test_values_3 <- c(NA,NA,NA,NA,1,1,1,1,1)
is_not_at_random <- function(x){
  with(rle(is.na(x)), sum(lengths[values] >= 4))
}
is_not_at_random(test_values_1)
[1] 0
is_not_at_random(test_values_2)
[1] 1
is_not_at_random(test_values_3)
[1] 1

Now, let’s check this identifies the peptides which are not missing at random. First, we’ll remove those without at least 4 quantification values.

peptides_no_mod_quant_4 <- peptides_no_mod_quant_no_lm[missing_n<=(ncol(peptides_no_mod_quant_no_lm)-4),]
missing_not_at_random <- apply(exprs(peptides_no_mod_quant_4), 1, is_not_at_random)
peptides_no_mod_quant_4[missing_not_at_random==1,] %>% plotMissing()
Out of 3847 total features, 3847 (100%) have missing values

   4    5    6    7    8    9   10   11 
  17   63  128  292  418  692  949 1288 
And 3847 features have more than one missing value

peptides_no_mod_quant_4[missing_not_at_random==2,] %>% plotMissing()
Out of 379 total features, 379 (100%) have missing values

  8   9  10  11 
  6  36  76 261 
And 379 features have more than one missing value

Now, let’s extend the function to replace the blocks of missing values with zero

test_values_1 <- c(1,1,NA,1,NA,NA,1,1,1,1)
test_values_2 <- c(1,1,NA,NA,NA,NA,NA,1,1,1)
test_values_3 <- c(NA,NA,NA,NA,1,1,1,1,1, NA)
rle(is.na(test_values_1))$values
[1] FALSE  TRUE FALSE  TRUE FALSE
rle(is.na(test_values_1))$lengths
[1] 2 1 1 2 4
replace_missing_not_at_random <- function(x){
  missing_rle <- rle(is.na(x))
  
  sequential_blocks <- missing_rle$values
  sequential_blocks[missing_rle$lengths<4] <- FALSE
  replace_with_zero <- rep(sequential_blocks, missing_rle$lengths)
  
  out <- x
  out[replace_with_zero]<-0
  
  return(out)
}
replace_missing_not_at_random(test_values_1)
 [1]  1  1 NA  1 NA NA  1  1  1  1
replace_missing_not_at_random(test_values_2)
 [1] 1 1 0 0 0 0 0 1 1 1
replace_missing_not_at_random(test_values_3)
 [1]  0  0  0  0  1  1  1  1  1 NA

Below we impute a maximum of 10 missing values in sequential blocks with zeros

missing_n2 <- rowSums(is.na(exprs(peptides_no_mod_quant_4)))
peptides_no_mod_quant_4_mnar_zero <- peptides_no_mod_quant_4[missing_n2<=10,]
exprs(peptides_no_mod_quant_4_mnar_zero) <- exprs(peptides_no_mod_quant_4_mnar_zero) %>%
  apply(1, replace_missing_not_at_random) %>% t()

Re-check the missing values

plotMissing(peptides_no_mod_quant_4)
Out of 6090 total features, 5945 (97.619%) have missing values

   1    2    3    4    5    6    7    8    9   10   11 
 121  188  233  267  291  332  478  594  816 1063 1562 
And 5824 features have more than one missing value

plotMissing(peptides_no_mod_quant_4_mnar_zero)
Out of 4528 total features, 3940 (87.014%) have missing values

  1   2   3   4   5   6   7   8   9  10 
639 792 740 586 415 286 186 170  88  38 
And 3301 features have more than one missing value

peptides_no_mod_quant_4_mnar_zero_mar_knn <- peptides_no_mod_quant_4_mnar_zero
imputed_zeros <- rowSums(exprs(peptides_no_mod_quant_4_mnar_zero_mar_knn)==0, na.rm=TRUE)
missing_n3 <- rowSums(is.na(exprs(peptides_no_mod_quant_4_mnar_zero_mar_knn)))
fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$zero_imputation <- imputed_zeros>0 
fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$zero_imputation_n <- imputed_zeros
fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing <- missing_n3>0 
fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing_n <- missing_n3
peptides_no_mod_quant_4_mnar_zero_mar_knn <- peptides_no_mod_quant_4_mnar_zero_mar_knn[missing_n3<=3,]
print(table(fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$zero_imputation))

FALSE  TRUE 
  687  2072 
print(table(fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing))

FALSE  TRUE 
  588  2171 
print(table(fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing,
            fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$zero_imputation))
       
        FALSE TRUE
  FALSE   145  443
  TRUE    542 1629
dim(peptides_no_mod_quant_4_mnar_zero)
[1] 4528   15
dim(peptides_no_mod_quant_4_mnar_zero_mar_knn)
[1] 2759   15
peptides_no_mod_quant_4_mnar_zero_mar_knn <- suppressMessages(impute(peptides_no_mod_quant_4_mnar_zero_mar_knn, "knn", k = 10))
Cluster size 2759 broken into 2532 227 
Cluster size 2532 broken into 2182 350 
Cluster size 2182 broken into 1982 200 
Cluster size 1982 broken into 1432 550 
Done cluster 1432 
Done cluster 550 
Done cluster 1982 
Done cluster 200 
Done cluster 2182 
Done cluster 350 
Done cluster 2532 
Done cluster 227 
p <- plotLabelQuant(peptides_no_mod_quant_no_lm, log=TRUE)

p <- plotLabelQuant(peptides_no_mod_quant_4_mnar_zero_mar_knn, log=TRUE)

p <- peptides_no_mod_quant_4_mnar_zero_mar_knn[
  fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing,] %>% plotLabelQuant(log=TRUE)

p <- peptides_no_mod_quant_4_mnar_zero_mar_knn[
  !fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing,] %>% plotLabelQuant(log=TRUE)

protein_quant <- agg_to_protein(peptides_no_mod_quant_4_mnar_zero_mar_knn)
print(dim(protein_quant))
[1] 739  15
source("~/git_repos/CamProt_R/LOPIT.R")
markers_df <- read.delim("./Input//markers_9B_hyperLOPIT_vs_DC.csv", sep=",", header=FALSE, stringsAsFactors=FALSE)[,1:2]
markers_df$V2 <- recode(markers_df$V2, "RIBOSOME 40S"="RIBOSOME", "RIBOSOME 60S"="RIBOSOME")
markers_proteins <- markers_df$V2
names(markers_proteins) <- markers_df$V1
protein_quant_am <- addMarkers(normalise(protein_quant, "sum"), markers_proteins)
Markers in data: 123 out of 739
organelleMarkers
          CYTOSOL                ER             GOLGI          LYSOSOME      MITOCHONDRIA           NUCLEUS 
                8                21                 4                 2                 5                16 
NUCLEUS-CHROMATIN        PEROXISOME                PM        PROTEASOME          RIBOSOME           unknown 
                6                 1                18                 3                39               616 
p <- plotHexbin(protein_quant_am, "markers")
print(p)

marker_classes <- getMarkerClasses(protein_quant_am, "markers")
m_colours <- getColours(marker_classes)
p <- plotPCA(protein_quant_am, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order)
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_1_2_inc_glycoproteins.png")
Saving 7.29 x 4.5 in image

p <- plotPCA(protein_quant_am, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, dims=c(3,4))
the condition has length > 1 and only the first element will be used
print(p)
$p

glycoproteins <- read.delim("./Input/glycoproteins.tsv")$protein

Copied from Manasa’s oopsFinal.Rmd notebook

# We will add a bit more information to the fData file 
# 1. List of known RBPs across cell lines in the XRNAX paper (Table S2)
xrnax = read.delim("./Input/xrnax-genelist.txt",sep="\t",header=T)
xrnax.rbps = xrnax %>% 
              dplyr::filter(!is.na(MCF7.RBP) | !is.na(HEK293.RBP) | !is.na(HeLa.RBP)) %>% 
              dplyr::select(Uniprot.ID:Protein.name)
rownames(xrnax.rbps) = xrnax.rbps$Uniprot.ID
print(length(rownames(xrnax.rbps)))
[1] 1753
# Check how many are common to the cell lines in the XRNAX paper
xrnax %>% 
  dplyr::select(MCF7.RBP:ihRBP) %>%
  apply(2, table,useNA="always")
            MCF7.RBP HEK293.RBP HeLa.RBP ihRBP
non-poly(A)      617        698      565   775
poly(A)          590        659      674   978
<NA>            1276       1126     1244   730
# 2. List of RBPs from SILAC experiments using OOPS after wash step2 (Table S1)
oops = read.delim("./Input/oops-genelist.txt",sep="\t",header=T)
oops.rbps = oops %>% 
              dplyr::filter(CL_NC_Ratio >= 1.0) %>% 
              dplyr::select(master_protein, RBP_glyco)
oops_rbps <- unique(oops.rbps$master_protein)
print(length(oops_rbps))
[1] 405
protein_quant_am_no_glyco <- protein_quant_am[!rownames(protein_quant_am) %in% glycoproteins,]
fData(protein_quant_am_no_glyco)$oops <- rownames(protein_quant_am_no_glyco) %in% oops_rbps
fData(protein_quant_am_no_glyco)$xrnax <- rownames(protein_quant_am_no_glyco) %in% rownames(xrnax.rbps)
fData(protein_quant_am_no_glyco)$go_rbp <- rownames(protein_quant_am_no_glyco) %in% GO_RBPs
print(table(fData(protein_quant_am_no_glyco)$oops, fData(protein_quant_am_no_glyco)$xrnax))
       
        FALSE TRUE
  FALSE   176  202
  TRUE     21  239
print(table(fData(protein_quant_am_no_glyco)$oops, fData(protein_quant_am_no_glyco)$xrnax,
            fData(protein_quant_am_no_glyco)$go_rbp))
, ,  = FALSE

       
        FALSE TRUE
  FALSE   149  104
  TRUE     16   31

, ,  = TRUE

       
        FALSE TRUE
  FALSE    27   98
  TRUE      5  208
print(dim(protein_quant_am))
[1] 739  15
print(dim(protein_quant_am_no_glyco))
[1] 638  15
marker_classes <- getMarkerClasses(protein_quant_am_no_glyco, "markers")
m_colours <- getColours(marker_classes)
p <- plotPCA(protein_quant_am_no_glyco, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order)
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_1_2.png")
Saving 7.29 x 4.5 in image

p <- plotPCA(protein_quant_am_no_glyco, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, dims=c(3,4))
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_3_4.png")
Saving 7.29 x 4.5 in image

protein_quant_am_no_glyco_yes_rbp <- protein_quant_am_no_glyco[
  (fData(protein_quant_am_no_glyco)$oops |
     fData(protein_quant_am_no_glyco)$xrnax |
    fData(protein_quant_am_no_glyco)$go_rbp),]
p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order)
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_1_2_no_glyco_only_rbps.png")
Saving 7.29 x 4.5 in image

p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, dims=c(3,4))
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_3_4_no_glyco_only_rbps.png")
Saving 7.29 x 4.5 in image

translocon <- human_go %>% filter(GO.ID=='GO:0071256') %>% pull(UNIPROTKB)
para <- human_go %>% filter(GO.ID=='GO:0042382') %>% pull(UNIPROTKB)
 
p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, foi=translocon)
the condition has length > 1 and only the first element will be used
print(p)
$p

$p_foi

ggsave("./Output/plots/pca_1_2_no_glyco_only_rbps_translocon.png")
Saving 7.29 x 4.5 in image

print(dim(protein_quant_am_no_glyco_yes_rbp))
[1] 489  15
p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, foi=para)
the condition has length > 1 and only the first element will be used
print(p)
$p

$p_foi

ggsave("./Output/plots/pca_1_2_no_glyco_only_rbps_paraspeckles.png")
Saving 7.29 x 4.5 in image

p <- PlotMarkerProfiles(protein_quant_am_no_glyco_yes_rbp, "markers", keep_markers=marker_classes, organelle_order=organelle_order) +
  facet_wrap(~markers) +
  scale_colour_manual(values=m_colours, guide=FALSE) +
  theme(legend.position="none")
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
print(p)

ggsave("./Output/plots/marker_profiles.png")
Saving 10 x 10 in image
p <- PlotMarkerProfiles(protein_quant_am_no_glyco_yes_rbp, "markers", keep_markers=marker_classes,
                        organelle_order=organelle_order, unknown=TRUE) +
  facet_grid(zero_imputation_n~missing_n) +
  scale_colour_manual(values=m_colours, guide=FALSE) +
  theme(legend.position="none")
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
print(p)

ggsave("./Output/plots/marker_profiles_imputation.png")
Saving 10 x 10 in image

Now we make a new set of markers designed to highlight functional groups of RBPs more usefully. First we need to define new sets of GO annotation proteins, where each marker belongs to only one group

translocon <- human_go %>% filter(GO.ID=='GO:0071256') %>% pull(UNIPROTKB)
para <- human_go %>% filter(GO.ID=='GO:0042382') %>% pull(UNIPROTKB)
mrna_splicing <- human_go %>% filter(GO.ID=='GO:0000398') %>% pull(UNIPROTKB)
translation_init <- human_go %>% filter(GO.ID=='GO:0006413') %>% pull(UNIPROTKB)
translation_init <- setdiff(translation_init, names(markers_proteins)[markers_proteins=="RIBOSOME"])
cell_cell_adhesion <- human_go %>% filter(GO.ID=='GO:0098609') %>% pull(UNIPROTKB)
cytoskeleton <- human_go %>% filter(GO.ID=='GO:0005856') %>% pull(UNIPROTKB)
motor_activity <- human_go %>% filter(GO.ID=='GO:0003774') %>% pull(UNIPROTKB)
er_stress_response <- human_go %>% filter(GO.ID=='GO:0030968') %>% pull(UNIPROTKB)
nuclear_pore_channel <- human_go %>% filter(GO.ID=='GO:0044613') %>% pull(UNIPROTKB)
nuclear_pore_basket <- human_go %>% filter(GO.ID=='GO:0044615') %>% pull(UNIPROTKB)
tRNA_AA <- human_go %>% filter(GO.ID=='GO:0004812') %>% pull(UNIPROTKB)
#mrna_splicing <- setdiff(mrna_splicing, c(para, translation_init, cell_cell_adhesion, cytoskeleton, motor_activity))
#translation_init <- setdiff(translation_init, c(para, cell_cell_adhesion, cytoskeleton, motor_activity))
#cytoskeleton <- setdiff(cytoskeleton, c(para, cell_cell_adhesion, motor_activity))
#cell_cell_adhesion <- setdiff(cell_cell_adhesion, c(para, motor_activity))
#all_markers <- c(mrna_splicing, para, translocon, translation_init, cell_cell_adhesion, cytoskeleton, motor_activity)
#print(length(all_markers)==length(unique(all_markers)))
mrna_splicing <- setdiff(mrna_splicing, tRNA_AA)
all_markers <- c(mrna_splicing, tRNA_AA)
print(length(all_markers)==length(unique(all_markers)))
[1] TRUE
rbps_markers <- markers_proteins
localisations_to_remove <- c("PEROXISOME", "PROTEASOME", "GOLGI", "LYSOSOME")
rbps_markers <- rbps_markers[!rbps_markers %in% localisations_to_remove]
rbps_markers[rbps_markers =="NUCLEUS-CHROMATIN"] <- "NUCLEUS"
print(table(rbps_markers))
rbps_markers
     CYTOSOL           ER MITOCHONDRIA      NUCLEUS           PM     RIBOSOME 
          96          118          197          171          295           72 
new_markers <- c(#rep("PARASPECKLES", length(para)),
                 rep("mRNA splicing", length(mrna_splicing)),
                 rep("Aminoacyl-tRNA ligase", length(tRNA_AA)),
                 "PARP1"#,
                 #rep("TRANSLATION INITITAION", length(translation_init)),
                 #rep("CELL-CELL ADHESION", length(cell_cell_adhesion)),
                 #rep("MOTOR", length(motor_activity))#,
                 #rep("CYTOSKELETON", length(cytoskeleton))
                 )
names(new_markers) <- c(#para,
                        mrna_splicing,
                        tRNA_AA,
                        "P09874"#,
                        #translation_init,
                        #cell_cell_adhesion,
                        #motor_activity#,
                        #cytoskeleton
                        )
print(table(names(new_markers))[table(names(new_markers))>1])
named integer(0)
rbps_markers <- rbps_markers[!names(rbps_markers) %in% names(new_markers)]
combined_markers <- c(rbps_markers, new_markers)
print(table(combined_markers))
combined_markers
Aminoacyl-tRNA ligase               CYTOSOL                    ER          MITOCHONDRIA         mRNA splicing 
                   42                    93                   118                   193                   321 
              NUCLEUS                 PARP1                    PM              RIBOSOME 
                  149                     1                   295                    72 
print(table(names(combined_markers))[table(names(combined_markers))>1])
named integer(0)
fData(protein_quant_am_no_glyco_yes_rbp)$new_markers <- NULL
protein_quant_am_no_glyco_yes_rbp <- addMarkers(protein_quant_am_no_glyco_yes_rbp, combined_markers, "new_markers")
Markers in data: 168 out of 489
organelleMarkers
Aminoacyl-tRNA ligase               CYTOSOL                    ER          MITOCHONDRIA         mRNA splicing 
                   13                     5                     7                     2                    86 
              NUCLEUS                 PARP1                    PM              RIBOSOME               unknown 
                   12                     1                     3                    39                   321 
fData(protein_quant_am_no_glyco_yes_rbp)$new_markers <- recode(
  fData(protein_quant_am_no_glyco_yes_rbp)$new_markers, "NUCLEUS"="Nucleus", "RIBOSOME"="Ribosome", "CYTOSOL"="Cytosol",
  "MITOCHONDRIA"="Mitochondria") 
print(table(fData(protein_quant_am_no_glyco_yes_rbp)$new_markers))

Aminoacyl-tRNA ligase               Cytosol                    ER          Mitochondria         mRNA splicing 
                   13                     5                     7                     2                    86 
              Nucleus                 PARP1                    PM              Ribosome               unknown 
                   12                     1                     3                    39                   321 

After adding these new RBP markers, we only have 1 PM and 2 Mt proteins remaining. Let’s remove the PM protein

fData(protein_quant_am_no_glyco_yes_rbp)$new_markers[fData(protein_quant_am_no_glyco_yes_rbp)$new_markers=="PM"] <- "unknown"
new_markers_levels <- getMarkerClasses(protein_quant_am_no_glyco_yes_rbp, "new_markers")
p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "new_markers", re_order_markers=TRUE, marker_levels=new_markers_levels,
        m_colours=getClassColours()[c(1:5, 7, 10:20)], foi=nuclear_pore_channel)
the condition has length > 1 and only the first element will be used
print(p)
$p

$p_foi

p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "new_markers", re_order_markers=TRUE, marker_levels=new_markers_levels,
        m_colours=getClassColours()[c(1:5, 7, 10:20)], foi=nuclear_pore_basket)
the condition has length > 1 and only the first element will be used
print(p)
$p

$p_foi

getMarkerClasses(protein_quant_am_no_glyco_yes_rbp, "new_markers")
[1] "Aminoacyl-tRNA ligase" "Cytosol"               "ER"                    "Mitochondria"         
[5] "mRNA splicing"         "Nucleus"               "PARP1"                 "Ribosome"             
set.seed(1)
proj <- make_proj("t-SNE", protein_quant_am_no_glyco_yes_rbp, "new_markers")
Loading required namespace: Rtsne
library(Hmisc)
Loading required package: lattice
Loading required package: survival

Attaching package: ‘survival’

The following object is masked from ‘package:robustbase’:

    heart

Loading required package: Formula

Attaching package: ‘Hmisc’

The following object is masked from ‘package:AnnotationDbi’:

    contents

The following objects are masked from ‘package:MSnbase’:

    impute, naplot

The following object is masked from ‘package:Biobase’:

    contents

The following objects are masked from ‘package:dplyr’:

    src, summarize

The following objects are masked from ‘package:base’:

    format.pval, units
proj_df <- proj$PCA_df
marker_levels <- setdiff(new_markers_levels, "unknown")
marker_levels <- marker_levels[c(2,3,4,6,8,1,5,7)]
#m_colours <- getStockcol()[c(1,7,4,3,2,5)]
m_colours <- c(cbPalette[c(6,3,2,4,8,7,5)], "grey20")
proj_df$markers <- factor(proj_df$new_markers, levels=marker_levels)
print(table(is.na(proj_df$markers)))

FALSE  TRUE 
  165   324 
proj_df$unknown <- proj_df$new_markers=="unknown"
p <- ggplot(proj_df, aes(X, Y, colour=markers)) +
    geom_point(aes(X, Y, colour=markers, alpha=unknown), size=2) +
    scale_alpha_manual(values=c(1,0.2), guide=F) +
    my_theme +
    scale_colour_manual(values=m_colours, na.value="grey60", name="", breaks=c(marker_levels)) +
    guides(colour = guide_legend(override.aes = list(alpha = 1, size=2))) +
    xlab("Dimension 1") + ylab("Dimension 2")
print(p)
ggsave("./Output/tSNE_RBP_markers.png")
Saving 7.29 x 4.5 in image

write.table(proj_df, "./Output/tSNE_projections.tsv", sep="\t", quote=FALSE, row.name=FALSE)
p <- PlotMarkerProfiles(protein_quant_am_no_glyco_yes_rbp, "new_markers",
                        keep_markers=getMarkerClasses(protein_quant_am_no_glyco_yes_rbp, "new_markers"), organelle_order=new_markers_levels) +
  facet_wrap(~markers) +
  scale_colour_manual(values=getClassColours()[c(1:5, 7, 10:20)], guide=FALSE) +
  theme(legend.position="none")
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
print(p)

protein_info <- read.delim("Input/Aggregated-proteins-2187-with-uniprot.tab") %>%
  dplyr::select(Entry, gene_name=Gene.names...primary..)
total.prot = readRDS("./Input/prot_res_20_fractions_imputed_markers.rds")
colnames(total.prot)[12] <- "0.948"
colnames(total.prot) <- c(seq(1,20,2), seq(2,20,2))
total.prot <- total.prot[,order(as.numeric(as.character(colnames(total.prot))))]
total.prot <- total.prot[,5:19]
total.prot = normalise(total.prot,"sum")
rbp.prot <- protein_quant_am_no_glyco_yes_rbp
colnames(rbp.prot) <- 5:19
print(dim(total.prot))
[1] 5610   15
print(dim(rbp.prot))
[1] 489  15
print(colnames(total.prot))
 [1] "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
print(colnames(rbp.prot))
 [1] "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
plotCombinedProfiles <- function(foi){
  
  foi_in_total <- intersect(foi, rownames(total.prot))
  foi_in_rbp <- intersect(foi, rownames(rbp.prot))
  
  foi_in_both <- intersect(foi_in_rbp, foi_in_total)
  
  print(foi_in_both)
  if(length(foi_in_both)==0){
    return(NA)
  }
  
  total_exprs_df <- exprs(total.prot[foi_in_both,])
  total_exprs_df <- melt(total_exprs_df)
  total_exprs_df$type = "All protein"
  
  rbp_exprs_df <- exprs(rbp.prot[foi_in_both,])
  rbp_exprs_df <- melt(rbp_exprs_df)
  rbp_exprs_df$type = "RNA-bound"
  
  #print(head(total_exprs_df))
  #print(head(rbp_exprs_df))
  #print(head(rbind(rbp_exprs_df, total_exprs_df)))
  
  p <- rbind(rbp_exprs_df, total_exprs_df) %>%
    merge(protein_info, by.x="Var1", by.y="Entry") %>%
    ggplot(aes(Var2, value, colour=type, group=type)) +
    my_theme + geom_line() +
    theme(aspect.ratio=1, axis.text.x=element_text(angle=90, vjust=0.5, hjust=1))  +
    xlab("Fraction") + ylab("Sumn normalised\nabundance") + 
    scale_colour_discrete(name="", na.value="grey")
  
  if(length(foi_in_both)>1){
    p <- p + facet_wrap(~gene_name)
  }
  
  print(p)
  
  p2 <- p <- rbind(rbp_exprs_df, total_exprs_df) %>%
    merge(protein_info, by.x="Var1", by.y="Entry") %>%
    ggplot(aes(Var2, value, colour=type, group=interaction(type, gene_name))) +
    my_theme + geom_line() +
    theme(aspect.ratio=1, axis.text.x=element_text(angle=90, vjust=0.5, hjust=1))  +
    xlab("Fraction") + ylab("Sumn normalised\nabundance") + 
    scale_colour_discrete(name="", na.value="grey")
  
  print(p2)
  
  invisible(list("p"=p, "p2"=p2))
}
p <- plotCombinedProfiles(para)
[1] "P23246" "Q15233" "P52272"

ggsave("Output/plots/paraspeckle.png", p$p2)
Saving 7.29 x 4.5 in image

plotCombinedProfiles(tRNA_AA)
 [1] "Q9NSE4" "P41252" "P54577" "P49589" "P49591" "P14868" "O43776" "P07814" "P47897" "P26639" "P26640" "P49588"
[13] "P41250"

plotCombinedProfiles(motor_activity)
[1] "Q14203" "P33176" "O00159" "Q14204" "P35579" "P52732" "Q13409" "P35580" "P60660"

fvarLabels(rbp.prot)
 [1] "Checked"                       "Confidence"                    "Sequence"                     
 [4] "Modifications"                 "Qvality.PEP"                   "Qvality.q.value"              
 [7] "Number.of.Protein.Groups"      "Number.of.Proteins"            "Number.of.PSMs"               
[10] "Master.Protein.Accessions"     "Number.of.Missed.Cleavages"    "Theo.MHplus.in.Da"            
[13] "XCorr.Sequest.HT"              "Confidence.Sequest.HT"         "Percolator.q.Value.Sequest.HT"
[16] "Percolator.PEP.Sequest.HT"     "master_protein"                "protein_length"               
[19] "protein_description"           "peptide_start"                 "peptide_end"                  
[22] "crap_protein"                  "associated_crap_protein"       "unique"                       
[25] "filename"                      "zero_imputation"               "zero_imputation_n"            
[28] "missing"                       "missing_n"                     "CV.F6"                        
[31] "CV.F7"                         "CV.F8"                         "CV.F9"                        
[34] "CV.F10"                        "CV.F11"                        "CV.F12"                       
[37] "CV.F13"                        "CV.F14"                        "CV.F15"                       
[40] "CV.F16"                        "CV.F17"                        "CV.F18"                       
[43] "CV.F19"                        "CV.F20"                        "markers"                      
[46] "oops"                          "xrnax"                         "go_rbp"                       
[49] "new_markers"                  
well_quantified_rbps <- fData(rbp.prot) %>%
  filter(zero_imputation_n<=4, missing_n<=2) %>%
  pull(master_protein)
plotCombinedProfiles(intersect(mrna_splicing, well_quantified_rbps))
[1] "Q01081" "Q00839" "P67809" "P11142" "O43290" "Q13247" "Q15366" "Q8IYB3"

ribosome_proteins <- names(markers_proteins)[markers_proteins=="RIBOSOME"]
plotCombinedProfiles(intersect(ribosome_proteins, well_quantified_rbps))
[1] "P15880" "P46781" "P35268" "P62750" "Q02543" "P36578" "P47914"

plotCombinedProfiles("Q15942")
[1] "Q15942"

p <- plotCombinedProfiles("P09874")

ggsave("./Output/PARP1_profiles.png", p$p2)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBub3RlYm9vayBpcyBUb20ncyBhbmFseXNpcyBmcm9tIHJhdyBkYXRhCgpgYGB7cn0Kc291cmNlKCIuLi9DYW1Qcm90X1IvVXRpbGl0eS5SIikKbGlicmFyeSh0aWR5dmVyc2UpCmluZmlsZSA8LSAiSW5wdXQvT09QU19xTE9QSVRfTGFiZWxGcmVlX1BlcHRpZGVHcm91cHNfcGFyc2VkLnR4dCIKc2FtcGxlc19pbmYgPC0gIklucHV0L3NhbXBsZXMudHN2IgpgYGAKCmBgYHtyfQpwZXB0aWRlc19kZiA8LSBwYXJzZV9mZWF0dXJlcyhpbmZpbGUsIHNpbGFjPUZBTFNFLCBUTVQ9RkFMU0UsIGxldmVsPSJwZXB0aWRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyX2NyYXA9VFJVRSwgcHJvdGVpbl9jb2w9J01hc3Rlci5Qcm90ZWluLkFjY2Vzc2lvbnMnKQpgYGAKCmBgYHtyfQpjb2xuYW1lcyhwZXB0aWRlc19kZikKCnBlcHRpZGVzX3F1YW50IDwtIG1ha2VNU05TZXQob2JqPXBlcHRpZGVzX2RmLCBzYW1wbGVzX2luZj1zYW1wbGVzX2luZiwgYWJfY29sX2l4PTIsIGxldmVsPSJwZXB0aWRlIiwgcXVhbnRfbmFtZT0iQXJlYSIpCgpgYGAKU28gbG90cyBhbmQgbG90cyBvZiBtaXNzaW5nIHZhbHVlcyEgbW9zdCBwZXB0aWRlcyBhcmUgcXVhbnRpZmllZCBpbiBvbmx5IGEgdmVyeSBtaW5vciBzdWJzZXQgb2YgZnJhY3Rpb25zICg8NS8yMCkuIFRoaXMgaXMgbm8gc3VwcmlzZSBzaW5jZSB3ZSdyZSBkZWFsaW5nIHdpdGggTEZRIGFuZCBzdWJjZWxsdWxhciBmcmFjdGlvbmF0aW9uIGhlcmUuCmBgYHtyfQpwbG90TWlzc2luZyhwZXB0aWRlc19xdWFudCkKYGBgCldoYXQgYWJvdXQgaWYgd2Ugc3Vic2V0IHRvIEdPIGFubm90YXRlZCBSQlBzPwpgYGB7cn0KaHVtYW5fZ28gPC0gcmVhZFJEUygiLi9JbnB1dC9oX3NhcGllbnNfZ29fZnVsbC5yZHMiKQpHT19SQlBzIDwtIGh1bWFuX2dvICU+JSBmaWx0ZXIoR08uSUQ9PSJHTzowMDAzNzIzIikgJT4lIHB1bGwoVU5JUFJPVEtCKQpgYGAKCmBgYHtyfQpwZXB0aWRlc19xdWFudFtmRGF0YShwZXB0aWRlc19xdWFudCkkbWFzdGVyX3Byb3RlaW4gJWluJSBHT19SQlBzLF0gJT4lIHBsb3RNaXNzaW5nKCkKCmBgYApPaywgc28gdGhpcyBkb2Vzbid0IG1ha2UgYW55IGRpZmZlcmVuY2UsIDk5LjklIGhhdmUgYSBtaXNzaW5nIHZhbHVlIQoKTGV0J3MgYWdncmVnYXRlIHRvIHRoZSB1bmlxdWUgcGVwdGlkZSBzZXF1ZW5jZSBsZXZlbCAoaWdub3JuaW5nIHZhcmlhYmxlIG1vZGlmaWNhdGlvbnMpIGFuZCB0aGVuIHNlZSB3aGV0aGVyIHRoYXQgcmVkdWNlcyBvdXIgcHJvYmxlbQoKYGBge3J9CnBlcHRpZGVzX25vX21vZF9xdWFudCA8LSBhZ2dfdG9fcGVwdGlkZXMocGVwdGlkZXNfcXVhbnQpCmBgYAoKTm9wZSwgbm90IHJlYWxseSEgV2Ugc3RpbGwgaGF2ZSAxOTMwNCBmZWF0dXJlcyAocHJldmlvdXNseSAyMTY4OCkgYW5kIDk5LjklIGhhdmUgbWlzc2luZyB2YWx1ZXMhCmBgYHtyfQpwbG90TWlzc2luZyhwZXB0aWRlc19ub19tb2RfcXVhbnQpCmBgYAoKT0ssIHNvIHdlJ3JlIGdvaW5nIHRvIGhhdmUgdG8gaW1wdXRlLiBOb3RlIHRoYXQgdGhlIG1pc3NpbmcgdmFsdWVzYSBhcmUgcGFydGljdWxhcmx5IHByZXNlbnQgaW4gdGhlIGZpcnN0IDIgZnJhY3Rpb25zIGFuZCBhY3Jvc3MgZnJhY3Rpb25zIDMtNy4gQWZ0ZXIgdGhhdCB3ZSBzZWUgZmV3ZXIgbWlzc2luZyB2YWx1ZXMuIEFsc28sIHJlbWVtYmVyIHRoYXQgd2UgaGF2ZSB5ZXQgdG8gaWRlbnRpZnkgYW55IGRlZmluaXRlIHNldCBvZiBSTkFzIGZyb20gdGhlIGluaXRpYWwgZnJhY3Rpb25zICgxLTdpc2gpLiBGb3IgdGhpcyByZWFzb24sIHdlJ2xsIHJlbW92ZSB0aGVzZSBmaXJzdCA1IGZyYWN0aW9ucyBmb3Igbm93IGFuZCBsZWF2ZSBmcmFjdGlvbiA2ICYgNyBhcyB0aGVzZSBhcmUgdXNlZnVsIHRvIHNlcGFyYXRlIGxpZ2h0IG1lbWJyYW5lcwpgYGB7cn0KcGxvdChjb2xTdW1zKGlzLm5hKGV4cHJzKHBlcHRpZGVzX25vX21vZF9xdWFudCkpKSkKYGBgCgoKYGBge3J9CiNwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0gPC0gcGVwdGlkZXNfbm9fbW9kX3F1YW50Wyw4OjIwXQpwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0gPC0gcGVwdGlkZXNfbm9fbW9kX3F1YW50Wyw2OjIwXQpwbG90TWlzc2luZyhwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0pCgpwbmcoIi4vT3V0cHV0L3Bsb3RzL21pc3NpbmdfdmFsdWVzX3BlcHRpZGUucG5nIikKcGxvdE1pc3NpbmcocGVwdGlkZXNfbm9fbW9kX3F1YW50X25vX2xtKQpkZXYub2ZmKCkKCmBgYApPaywgc28gbm93IHdlJ3JlIGRvd24gdG8gX2p1c3RfIDk5LjElIG1pc3NpbmcgOnAgYnV0IGF0IGxlYXN0IHdlIGhhdmUgc29tZSBtb3JlIHBlcHRpZGVzIG5vdyB3aXRoIHJlbGF0aXZlbHkgZmV3ICg8PTQgbWlzc2luZyB2YWx1ZXMpCgpJZiB3ZSBmb2N1cyBvbiB0aG9zZSBwZXB0aWRlcyB3aXRoIDQtOSBtaXNzaW5nIHZhbHVlcywgd2UgY2FuIHNlZSB0aGF0IG1hbnkgYXJlIG1pc3NpbmcgaW4gYSBibG9jayBvZiBzZXF1ZW50aWFsIGZyYWN0aW9ucy4gQXJndWFibHksIHRoZXNlIGFyZSB0cnVlIG1pc3NpbmcgdmFsdWVzLCBlLmcgbm90IGF0IHJhbmRvbS4gCmBgYHtyfQptaXNzaW5nX24gPC0gcm93U3Vtcyhpcy5uYShleHBycyhwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0pKSkKcGVwdGlkZXNfbm9fbW9kX3F1YW50X25vX2xtWyhtaXNzaW5nX24+PTQgJiBtaXNzaW5nX248PTkpLF0gJT4lIHBsb3RNaXNzaW5nKCkKYGBgCldlIGNhbiBpZGVudGlmeSB0aGUgbWlzc2luZyB2YWx1ZXMgd2hpY2ggYXJlIGluIGEgc2VxdWVudGlhbCBibG9jayBvZiA+PTUgZnJhY3Rpb25zIGluIGEgcm93IGFuZCByZXBsYWNlIHRoZXNlIHdpdGggemVybwoKRmlyc3QsIGxldCdzIG1ha2UgYSBmdW5jdGlvbiB0byBpZGVudGlmeSByb3dzIG9mIHZhbHVlcyB3aGVyZSB0aGUgbWlzc2luZyB2YWx1ZXMgYXJlIG5vdCByYW5kb20sIGUuZyA0IG9yIG1vcmUgaW4gYSByb3cKYGBge3J9CnRlc3RfdmFsdWVzXzEgPC0gYygxLDEsTkEsMSxOQSxOQSwxLDEsMSwxKQp0ZXN0X3ZhbHVlc18yIDwtIGMoMSwxLE5BLE5BLE5BLE5BLE5BLDEsMSwxKQp0ZXN0X3ZhbHVlc18zIDwtIGMoTkEsTkEsTkEsTkEsMSwxLDEsMSwxKQoKaXNfbm90X2F0X3JhbmRvbSA8LSBmdW5jdGlvbih4KXsKICB3aXRoKHJsZShpcy5uYSh4KSksIHN1bShsZW5ndGhzW3ZhbHVlc10gPj0gNCkpCn0KCmlzX25vdF9hdF9yYW5kb20odGVzdF92YWx1ZXNfMSkKaXNfbm90X2F0X3JhbmRvbSh0ZXN0X3ZhbHVlc18yKQppc19ub3RfYXRfcmFuZG9tKHRlc3RfdmFsdWVzXzMpCmBgYAoKTm93LCBsZXQncyBjaGVjayB0aGlzIGlkZW50aWZpZXMgdGhlIHBlcHRpZGVzIHdoaWNoIGFyZSBub3QgbWlzc2luZyBhdCByYW5kb20uIEZpcnN0LCB3ZSdsbCByZW1vdmUgdGhvc2Ugd2l0aG91dCBhdCBsZWFzdCA0IHF1YW50aWZpY2F0aW9uIHZhbHVlcy4KYGBge3J9CnBlcHRpZGVzX25vX21vZF9xdWFudF80IDwtIHBlcHRpZGVzX25vX21vZF9xdWFudF9ub19sbVttaXNzaW5nX248PShuY29sKHBlcHRpZGVzX25vX21vZF9xdWFudF9ub19sbSktNCksXQptaXNzaW5nX25vdF9hdF9yYW5kb20gPC0gYXBwbHkoZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzQpLCAxLCBpc19ub3RfYXRfcmFuZG9tKQpgYGAKCmBgYHtyfQpwZXB0aWRlc19ub19tb2RfcXVhbnRfNFttaXNzaW5nX25vdF9hdF9yYW5kb209PTEsXSAlPiUgcGxvdE1pc3NpbmcoKQpwZXB0aWRlc19ub19tb2RfcXVhbnRfNFttaXNzaW5nX25vdF9hdF9yYW5kb209PTIsXSAlPiUgcGxvdE1pc3NpbmcoKQpgYGAKTm93LCBsZXQncyBleHRlbmQgdGhlIGZ1bmN0aW9uIHRvIHJlcGxhY2UgdGhlIGJsb2NrcyBvZiBtaXNzaW5nIHZhbHVlcyB3aXRoIHplcm8KYGBge3J9CnRlc3RfdmFsdWVzXzEgPC0gYygxLDEsTkEsMSxOQSxOQSwxLDEsMSwxKQp0ZXN0X3ZhbHVlc18yIDwtIGMoMSwxLE5BLE5BLE5BLE5BLE5BLDEsMSwxKQp0ZXN0X3ZhbHVlc18zIDwtIGMoTkEsTkEsTkEsTkEsMSwxLDEsMSwxLCBOQSkKCgpybGUoaXMubmEodGVzdF92YWx1ZXNfMSkpJHZhbHVlcwpybGUoaXMubmEodGVzdF92YWx1ZXNfMSkpJGxlbmd0aHMKCnJlcGxhY2VfbWlzc2luZ19ub3RfYXRfcmFuZG9tIDwtIGZ1bmN0aW9uKHgpewogIG1pc3NpbmdfcmxlIDwtIHJsZShpcy5uYSh4KSkKICAKICBzZXF1ZW50aWFsX2Jsb2NrcyA8LSBtaXNzaW5nX3JsZSR2YWx1ZXMKICBzZXF1ZW50aWFsX2Jsb2Nrc1ttaXNzaW5nX3JsZSRsZW5ndGhzPDRdIDwtIEZBTFNFCgogIHJlcGxhY2Vfd2l0aF96ZXJvIDwtIHJlcChzZXF1ZW50aWFsX2Jsb2NrcywgbWlzc2luZ19ybGUkbGVuZ3RocykKICAKICBvdXQgPC0geAogIG91dFtyZXBsYWNlX3dpdGhfemVyb108LTAKICAKICByZXR1cm4ob3V0KQoKfQoKcmVwbGFjZV9taXNzaW5nX25vdF9hdF9yYW5kb20odGVzdF92YWx1ZXNfMSkKcmVwbGFjZV9taXNzaW5nX25vdF9hdF9yYW5kb20odGVzdF92YWx1ZXNfMikKcmVwbGFjZV9taXNzaW5nX25vdF9hdF9yYW5kb20odGVzdF92YWx1ZXNfMykKYGBgCgoKQmVsb3cgd2UgaW1wdXRlIGEgbWF4aW11bSBvZiAxMCBtaXNzaW5nIHZhbHVlcyBpbiBzZXF1ZW50aWFsIGJsb2NrcyB3aXRoIHplcm9zCmBgYHtyfQptaXNzaW5nX24yIDwtIHJvd1N1bXMoaXMubmEoZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzQpKSkKcGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvIDwtIHBlcHRpZGVzX25vX21vZF9xdWFudF80W21pc3NpbmdfbjI8PTEwLF0KCmV4cHJzKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVybykgPC0gZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvKSAlPiUKICBhcHBseSgxLCByZXBsYWNlX21pc3Npbmdfbm90X2F0X3JhbmRvbSkgJT4lIHQoKQpgYGAKClJlLWNoZWNrIHRoZSBtaXNzaW5nIHZhbHVlcwpgYGB7cn0KcGxvdE1pc3NpbmcocGVwdGlkZXNfbm9fbW9kX3F1YW50XzQpCnBsb3RNaXNzaW5nKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVybykKYGBgCgpgYGB7cn0KcGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4gPC0gcGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvCgppbXB1dGVkX3plcm9zIDwtIHJvd1N1bXMoZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pPT0wLCBuYS5ybT1UUlVFKQptaXNzaW5nX24zIDwtIHJvd1N1bXMoaXMubmEoZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pKSkKCmZEYXRhKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uKSR6ZXJvX2ltcHV0YXRpb24gPC0gaW1wdXRlZF96ZXJvcz4wIApmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkemVyb19pbXB1dGF0aW9uX24gPC0gaW1wdXRlZF96ZXJvcwoKZkRhdGEocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pJG1pc3NpbmcgPC0gbWlzc2luZ19uMz4wIApmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkbWlzc2luZ19uIDwtIG1pc3NpbmdfbjMKCgpwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubiA8LSBwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tublttaXNzaW5nX24zPD0zLF0KCnByaW50KHRhYmxlKGZEYXRhKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uKSR6ZXJvX2ltcHV0YXRpb24pKQpwcmludCh0YWJsZShmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkbWlzc2luZykpCgpwcmludCh0YWJsZShmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkbWlzc2luZywKICAgICAgICAgICAgZkRhdGEocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pJHplcm9faW1wdXRhdGlvbikpCgpkaW0ocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvKQpkaW0ocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pCnBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uIDwtIHN1cHByZXNzTWVzc2FnZXMoaW1wdXRlKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uLCAia25uIiwgayA9IDEwKSkKYGBgCgpgYGB7cn0KcCA8LSBwbG90TGFiZWxRdWFudChwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0sIGxvZz1UUlVFKQpwIDwtIHBsb3RMYWJlbFF1YW50KHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uLCBsb2c9VFJVRSkKYGBgCgpgYGB7cn0KcCA8LSBwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tublsKICBmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkbWlzc2luZyxdICU+JSBwbG90TGFiZWxRdWFudChsb2c9VFJVRSkKcCA8LSBwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tublsKICAhZkRhdGEocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pJG1pc3NpbmcsXSAlPiUgcGxvdExhYmVsUXVhbnQobG9nPVRSVUUpCmBgYAoKYGBge3J9CnByb3RlaW5fcXVhbnQgPC0gYWdnX3RvX3Byb3RlaW4ocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pCnByaW50KGRpbShwcm90ZWluX3F1YW50KSkKYGBgCgpgYGB7cn0Kc291cmNlKCJ+L2dpdF9yZXBvcy9DYW1Qcm90X1IvTE9QSVQuUiIpCmBgYAoKYGBge3J9Cm1hcmtlcnNfZGYgPC0gcmVhZC5kZWxpbSgiLi9JbnB1dC8vbWFya2Vyc185Ql9oeXBlckxPUElUX3ZzX0RDLmNzdiIsIHNlcD0iLCIsIGhlYWRlcj1GQUxTRSwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSlbLDE6Ml0KbWFya2Vyc19kZiRWMiA8LSByZWNvZGUobWFya2Vyc19kZiRWMiwgIlJJQk9TT01FIDQwUyI9IlJJQk9TT01FIiwgIlJJQk9TT01FIDYwUyI9IlJJQk9TT01FIikKbWFya2Vyc19wcm90ZWlucyA8LSBtYXJrZXJzX2RmJFYyCm5hbWVzKG1hcmtlcnNfcHJvdGVpbnMpIDwtIG1hcmtlcnNfZGYkVjEKCnByb3RlaW5fcXVhbnRfYW0gPC0gYWRkTWFya2Vycyhub3JtYWxpc2UocHJvdGVpbl9xdWFudCwgInN1bSIpLCBtYXJrZXJzX3Byb3RlaW5zKQoKcCA8LSBwbG90SGV4YmluKHByb3RlaW5fcXVhbnRfYW0sICJtYXJrZXJzIikKcHJpbnQocCkKYGBgCgoKYGBge3J9CgptYXJrZXJfY2xhc3NlcyA8LSBnZXRNYXJrZXJDbGFzc2VzKHByb3RlaW5fcXVhbnRfYW0sICJtYXJrZXJzIikKbV9jb2xvdXJzIDwtIGdldENvbG91cnMobWFya2VyX2NsYXNzZXMpCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtLCAibWFya2VycyIsIG1fY29sb3Vycz1tX2NvbG91cnMsIHJlX29yZGVyX21hcmtlcnM9VFJVRSwgbWFya2VyX2xldmVscz1vcmdhbmVsbGVfb3JkZXIpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzFfMl9pbmNfZ2x5Y29wcm90ZWlucy5wbmciKQoKcCA8LSBwbG90UENBKHByb3RlaW5fcXVhbnRfYW0sICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlciwgZGltcz1jKDMsNCkpCnByaW50KHApCmBgYAoKYGBge3J9CmdseWNvcHJvdGVpbnMgPC0gcmVhZC5kZWxpbSgiLi9JbnB1dC9nbHljb3Byb3RlaW5zLnRzdiIpJHByb3RlaW4KYGBgCgpDb3BpZWQgZnJvbSBNYW5hc2EncyBvb3BzRmluYWwuUm1kIG5vdGVib29rCmBgYHtyfQojIFdlIHdpbGwgYWRkIGEgYml0IG1vcmUgaW5mb3JtYXRpb24gdG8gdGhlIGZEYXRhIGZpbGUgCiMgMS4gTGlzdCBvZiBrbm93biBSQlBzIGFjcm9zcyBjZWxsIGxpbmVzIGluIHRoZSBYUk5BWCBwYXBlciAoVGFibGUgUzIpCnhybmF4ID0gcmVhZC5kZWxpbSgiLi9JbnB1dC94cm5heC1nZW5lbGlzdC50eHQiLHNlcD0iXHQiLGhlYWRlcj1UKQp4cm5heC5yYnBzID0geHJuYXggJT4lIAogICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKE1DRjcuUkJQKSB8ICFpcy5uYShIRUsyOTMuUkJQKSB8ICFpcy5uYShIZUxhLlJCUCkpICU+JSAKICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KFVuaXByb3QuSUQ6UHJvdGVpbi5uYW1lKQpyb3duYW1lcyh4cm5heC5yYnBzKSA9IHhybmF4LnJicHMkVW5pcHJvdC5JRApwcmludChsZW5ndGgocm93bmFtZXMoeHJuYXgucmJwcykpKQoKIyBDaGVjayBob3cgbWFueSBhcmUgY29tbW9uIHRvIHRoZSBjZWxsIGxpbmVzIGluIHRoZSBYUk5BWCBwYXBlcgp4cm5heCAlPiUgCiAgZHBseXI6OnNlbGVjdChNQ0Y3LlJCUDppaFJCUCkgJT4lCiAgYXBwbHkoMiwgdGFibGUsdXNlTkE9ImFsd2F5cyIpCgojIDIuIExpc3Qgb2YgUkJQcyBmcm9tIFNJTEFDIGV4cGVyaW1lbnRzIHVzaW5nIE9PUFMgYWZ0ZXIgd2FzaCBzdGVwMiAoVGFibGUgUzEpCm9vcHMgPSByZWFkLmRlbGltKCIuL0lucHV0L29vcHMtZ2VuZWxpc3QudHh0IixzZXA9Ilx0IixoZWFkZXI9VCkKb29wcy5yYnBzID0gb29wcyAlPiUgCiAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihDTF9OQ19SYXRpbyA+PSAxLjApICU+JSAKICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KG1hc3Rlcl9wcm90ZWluLCBSQlBfZ2x5Y28pCgpvb3BzX3JicHMgPC0gdW5pcXVlKG9vcHMucmJwcyRtYXN0ZXJfcHJvdGVpbikKcHJpbnQobGVuZ3RoKG9vcHNfcmJwcykpCmBgYAoKYGBge3J9CnByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28gPC0gcHJvdGVpbl9xdWFudF9hbVshcm93bmFtZXMocHJvdGVpbl9xdWFudF9hbSkgJWluJSBnbHljb3Byb3RlaW5zLF0KZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljbykkb29wcyA8LSByb3duYW1lcyhwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSAlaW4lIG9vcHNfcmJwcwpmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSR4cm5heCA8LSByb3duYW1lcyhwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSAlaW4lIHJvd25hbWVzKHhybmF4LnJicHMpCmZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28pJGdvX3JicCA8LSByb3duYW1lcyhwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSAlaW4lIEdPX1JCUHMKCnByaW50KHRhYmxlKGZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28pJG9vcHMsIGZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28pJHhybmF4KSkKcHJpbnQodGFibGUoZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljbykkb29wcywgZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljbykkeHJuYXgsCiAgICAgICAgICAgIGZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28pJGdvX3JicCkpCgpwcmludChkaW0ocHJvdGVpbl9xdWFudF9hbSkpCnByaW50KGRpbShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSkKYGBgCmBgYHtyfQptYXJrZXJfY2xhc3NlcyA8LSBnZXRNYXJrZXJDbGFzc2VzKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28sICJtYXJrZXJzIikKbV9jb2xvdXJzIDwtIGdldENvbG91cnMobWFya2VyX2NsYXNzZXMpCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvLCAibWFya2VycyIsIG1fY29sb3Vycz1tX2NvbG91cnMsIHJlX29yZGVyX21hcmtlcnM9VFJVRSwgbWFya2VyX2xldmVscz1vcmdhbmVsbGVfb3JkZXIpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzFfMi5wbmciKQoKcCA8LSBwbG90UENBKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28sICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlciwgZGltcz1jKDMsNCkpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzNfNC5wbmciKQpgYGAKYGBge3J9CnByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCA8LSBwcm90ZWluX3F1YW50X2FtX25vX2dseWNvWwogIChmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSRvb3BzIHwKICAgICBmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSR4cm5heCB8CiAgICBmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSRnb19yYnApLF0KCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlcikKcHJpbnQocCkKZ2dzYXZlKCIuL091dHB1dC9wbG90cy9wY2FfMV8yX25vX2dseWNvX29ubHlfcmJwcy5wbmciKQoKCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlciwgZGltcz1jKDMsNCkpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzNfNF9ub19nbHljb19vbmx5X3JicHMucG5nIikKYGBgCgpgYGB7cn0KdHJhbnNsb2NvbiA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA3MTI1NicpICU+JSBwdWxsKFVOSVBST1RLQikKcGFyYSA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA0MjM4MicpICU+JSBwdWxsKFVOSVBST1RLQikKCiAKYGBgCgpgYGB7cn0KcCA8LSBwbG90UENBKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCwgIm1hcmtlcnMiLCBtX2NvbG91cnM9bV9jb2xvdXJzLCByZV9vcmRlcl9tYXJrZXJzPVRSVUUsIG1hcmtlcl9sZXZlbHM9b3JnYW5lbGxlX29yZGVyLCBmb2k9dHJhbnNsb2NvbikKcHJpbnQocCkKZ2dzYXZlKCIuL091dHB1dC9wbG90cy9wY2FfMV8yX25vX2dseWNvX29ubHlfcmJwc190cmFuc2xvY29uLnBuZyIpCmBgYApgYGB7cn0KcHJpbnQoZGltKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCkpCmBgYAoKYGBge3J9CnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlciwgZm9pPXBhcmEpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzFfMl9ub19nbHljb19vbmx5X3JicHNfcGFyYXNwZWNrbGVzLnBuZyIpCmBgYAoKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpwIDwtIFBsb3RNYXJrZXJQcm9maWxlcyhwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJtYXJrZXJzIiwga2VlcF9tYXJrZXJzPW1hcmtlcl9jbGFzc2VzLCBvcmdhbmVsbGVfb3JkZXI9b3JnYW5lbGxlX29yZGVyKSArCiAgZmFjZXRfd3JhcCh+bWFya2VycykgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPW1fY29sb3VycywgZ3VpZGU9RkFMU0UpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwcmludChwKQpnZ3NhdmUoIi4vT3V0cHV0L3Bsb3RzL21hcmtlcl9wcm9maWxlcy5wbmciKQoKcCA8LSBQbG90TWFya2VyUHJvZmlsZXMocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwLCAibWFya2VycyIsIGtlZXBfbWFya2Vycz1tYXJrZXJfY2xhc3NlcywKICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5lbGxlX29yZGVyPW9yZ2FuZWxsZV9vcmRlciwgdW5rbm93bj1UUlVFKSArCiAgZmFjZXRfZ3JpZCh6ZXJvX2ltcHV0YXRpb25fbn5taXNzaW5nX24pICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1tX2NvbG91cnMsIGd1aWRlPUZBTFNFKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKcHJpbnQocCkKZ2dzYXZlKCIuL091dHB1dC9wbG90cy9tYXJrZXJfcHJvZmlsZXNfaW1wdXRhdGlvbi5wbmciKQpgYGAKCk5vdyB3ZSBtYWtlIGEgbmV3IHNldCBvZiBtYXJrZXJzIGRlc2lnbmVkIHRvIGhpZ2hsaWdodCBmdW5jdGlvbmFsIGdyb3VwcyBvZiBSQlBzIG1vcmUgdXNlZnVsbHkuIEZpcnN0IHdlIG5lZWQgdG8gZGVmaW5lIG5ldyBzZXRzIG9mIEdPIGFubm90YXRpb24gcHJvdGVpbnMsIHdoZXJlIGVhY2ggbWFya2VyIGJlbG9uZ3MgdG8gb25seSBvbmUgZ3JvdXAKYGBge3J9CnRyYW5zbG9jb24gPC0gaHVtYW5fZ28gJT4lIGZpbHRlcihHTy5JRD09J0dPOjAwNzEyNTYnKSAlPiUgcHVsbChVTklQUk9US0IpCnBhcmEgPC0gaHVtYW5fZ28gJT4lIGZpbHRlcihHTy5JRD09J0dPOjAwNDIzODInKSAlPiUgcHVsbChVTklQUk9US0IpCm1ybmFfc3BsaWNpbmcgPC0gaHVtYW5fZ28gJT4lIGZpbHRlcihHTy5JRD09J0dPOjAwMDAzOTgnKSAlPiUgcHVsbChVTklQUk9US0IpCnRyYW5zbGF0aW9uX2luaXQgPC0gaHVtYW5fZ28gJT4lIGZpbHRlcihHTy5JRD09J0dPOjAwMDY0MTMnKSAlPiUgcHVsbChVTklQUk9US0IpCnRyYW5zbGF0aW9uX2luaXQgPC0gc2V0ZGlmZih0cmFuc2xhdGlvbl9pbml0LCBuYW1lcyhtYXJrZXJzX3Byb3RlaW5zKVttYXJrZXJzX3Byb3RlaW5zPT0iUklCT1NPTUUiXSkKCmNlbGxfY2VsbF9hZGhlc2lvbiA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA5ODYwOScpICU+JSBwdWxsKFVOSVBST1RLQikKY3l0b3NrZWxldG9uIDwtIGh1bWFuX2dvICU+JSBmaWx0ZXIoR08uSUQ9PSdHTzowMDA1ODU2JykgJT4lIHB1bGwoVU5JUFJPVEtCKQptb3Rvcl9hY3Rpdml0eSA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDAwMzc3NCcpICU+JSBwdWxsKFVOSVBST1RLQikKZXJfc3RyZXNzX3Jlc3BvbnNlIDwtIGh1bWFuX2dvICU+JSBmaWx0ZXIoR08uSUQ9PSdHTzowMDMwOTY4JykgJT4lIHB1bGwoVU5JUFJPVEtCKQpudWNsZWFyX3BvcmVfY2hhbm5lbCA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA0NDYxMycpICU+JSBwdWxsKFVOSVBST1RLQikKbnVjbGVhcl9wb3JlX2Jhc2tldCA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA0NDYxNScpICU+JSBwdWxsKFVOSVBST1RLQikKdFJOQV9BQSA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDAwNDgxMicpICU+JSBwdWxsKFVOSVBST1RLQikKCiNtcm5hX3NwbGljaW5nIDwtIHNldGRpZmYobXJuYV9zcGxpY2luZywgYyhwYXJhLCB0cmFuc2xhdGlvbl9pbml0LCBjZWxsX2NlbGxfYWRoZXNpb24sIGN5dG9za2VsZXRvbiwgbW90b3JfYWN0aXZpdHkpKQojdHJhbnNsYXRpb25faW5pdCA8LSBzZXRkaWZmKHRyYW5zbGF0aW9uX2luaXQsIGMocGFyYSwgY2VsbF9jZWxsX2FkaGVzaW9uLCBjeXRvc2tlbGV0b24sIG1vdG9yX2FjdGl2aXR5KSkKI2N5dG9za2VsZXRvbiA8LSBzZXRkaWZmKGN5dG9za2VsZXRvbiwgYyhwYXJhLCBjZWxsX2NlbGxfYWRoZXNpb24sIG1vdG9yX2FjdGl2aXR5KSkKI2NlbGxfY2VsbF9hZGhlc2lvbiA8LSBzZXRkaWZmKGNlbGxfY2VsbF9hZGhlc2lvbiwgYyhwYXJhLCBtb3Rvcl9hY3Rpdml0eSkpCgojYWxsX21hcmtlcnMgPC0gYyhtcm5hX3NwbGljaW5nLCBwYXJhLCB0cmFuc2xvY29uLCB0cmFuc2xhdGlvbl9pbml0LCBjZWxsX2NlbGxfYWRoZXNpb24sIGN5dG9za2VsZXRvbiwgbW90b3JfYWN0aXZpdHkpCiNwcmludChsZW5ndGgoYWxsX21hcmtlcnMpPT1sZW5ndGgodW5pcXVlKGFsbF9tYXJrZXJzKSkpCgoKbXJuYV9zcGxpY2luZyA8LSBzZXRkaWZmKG1ybmFfc3BsaWNpbmcsIHRSTkFfQUEpCgphbGxfbWFya2VycyA8LSBjKG1ybmFfc3BsaWNpbmcsIHRSTkFfQUEpCnByaW50KGxlbmd0aChhbGxfbWFya2Vycyk9PWxlbmd0aCh1bmlxdWUoYWxsX21hcmtlcnMpKSkKCmBgYAoKCgoKCmBgYHtyfQoKcmJwc19tYXJrZXJzIDwtIG1hcmtlcnNfcHJvdGVpbnMKbG9jYWxpc2F0aW9uc190b19yZW1vdmUgPC0gYygiUEVST1hJU09NRSIsICJQUk9URUFTT01FIiwgIkdPTEdJIiwgIkxZU09TT01FIikKCnJicHNfbWFya2VycyA8LSByYnBzX21hcmtlcnNbIXJicHNfbWFya2VycyAlaW4lIGxvY2FsaXNhdGlvbnNfdG9fcmVtb3ZlXQpyYnBzX21hcmtlcnNbcmJwc19tYXJrZXJzID09Ik5VQ0xFVVMtQ0hST01BVElOIl0gPC0gIk5VQ0xFVVMiCgpwcmludCh0YWJsZShyYnBzX21hcmtlcnMpKQoKCm5ld19tYXJrZXJzIDwtIGMoI3JlcCgiUEFSQVNQRUNLTEVTIiwgbGVuZ3RoKHBhcmEpKSwKICAgICAgICAgICAgICAgICByZXAoIm1STkEgc3BsaWNpbmciLCBsZW5ndGgobXJuYV9zcGxpY2luZykpLAogICAgICAgICAgICAgICAgIHJlcCgiQW1pbm9hY3lsLXRSTkEgbGlnYXNlIiwgbGVuZ3RoKHRSTkFfQUEpKSwKICAgICAgICAgICAgICAgICAiUEFSUDEiIywKICAgICAgICAgICAgICAgICAjcmVwKCJUUkFOU0xBVElPTiBJTklUSVRBSU9OIiwgbGVuZ3RoKHRyYW5zbGF0aW9uX2luaXQpKSwKICAgICAgICAgICAgICAgICAjcmVwKCJDRUxMLUNFTEwgQURIRVNJT04iLCBsZW5ndGgoY2VsbF9jZWxsX2FkaGVzaW9uKSksCiAgICAgICAgICAgICAgICAgI3JlcCgiTU9UT1IiLCBsZW5ndGgobW90b3JfYWN0aXZpdHkpKSMsCiAgICAgICAgICAgICAgICAgI3JlcCgiQ1lUT1NLRUxFVE9OIiwgbGVuZ3RoKGN5dG9za2VsZXRvbikpCiAgICAgICAgICAgICAgICAgKQoKbmFtZXMobmV3X21hcmtlcnMpIDwtIGMoI3BhcmEsCiAgICAgICAgICAgICAgICAgICAgICAgIG1ybmFfc3BsaWNpbmcsCiAgICAgICAgICAgICAgICAgICAgICAgIHRSTkFfQUEsCiAgICAgICAgICAgICAgICAgICAgICAgICJQMDk4NzQiIywKICAgICAgICAgICAgICAgICAgICAgICAgI3RyYW5zbGF0aW9uX2luaXQsCiAgICAgICAgICAgICAgICAgICAgICAgICNjZWxsX2NlbGxfYWRoZXNpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICNtb3Rvcl9hY3Rpdml0eSMsCiAgICAgICAgICAgICAgICAgICAgICAgICNjeXRvc2tlbGV0b24KICAgICAgICAgICAgICAgICAgICAgICAgKQpwcmludCh0YWJsZShuYW1lcyhuZXdfbWFya2VycykpW3RhYmxlKG5hbWVzKG5ld19tYXJrZXJzKSk+MV0pCgpyYnBzX21hcmtlcnMgPC0gcmJwc19tYXJrZXJzWyFuYW1lcyhyYnBzX21hcmtlcnMpICVpbiUgbmFtZXMobmV3X21hcmtlcnMpXQoKY29tYmluZWRfbWFya2VycyA8LSBjKHJicHNfbWFya2VycywgbmV3X21hcmtlcnMpCnByaW50KHRhYmxlKGNvbWJpbmVkX21hcmtlcnMpKQpwcmludCh0YWJsZShuYW1lcyhjb21iaW5lZF9tYXJrZXJzKSlbdGFibGUobmFtZXMoY29tYmluZWRfbWFya2VycykpPjFdKQoKZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwKSRuZXdfbWFya2VycyA8LSBOVUxMCnByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCA8LSBhZGRNYXJrZXJzKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCwgY29tYmluZWRfbWFya2VycywgIm5ld19tYXJrZXJzIikKCmZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCkkbmV3X21hcmtlcnMgPC0gcmVjb2RlKAogIGZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCkkbmV3X21hcmtlcnMsICJOVUNMRVVTIj0iTnVjbGV1cyIsICJSSUJPU09NRSI9IlJpYm9zb21lIiwgIkNZVE9TT0wiPSJDeXRvc29sIiwKICAiTUlUT0NIT05EUklBIj0iTWl0b2Nob25kcmlhIikgCgpwcmludCh0YWJsZShmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnApJG5ld19tYXJrZXJzKSkKYGBgCmBgYHtyfQoKYGBgCgoKQWZ0ZXIgYWRkaW5nIHRoZXNlIG5ldyBSQlAgbWFya2Vycywgd2Ugb25seSBoYXZlIDEgUE0gYW5kIDIgTXQgcHJvdGVpbnMgcmVtYWluaW5nLiBMZXQncyByZW1vdmUgdGhlIFBNIHByb3RlaW4KYGBge3J9CmZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCkkbmV3X21hcmtlcnNbZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwKSRuZXdfbWFya2Vycz09IlBNIl0gPC0gInVua25vd24iCmBgYAoKYGBge3J9Cm5ld19tYXJrZXJzX2xldmVscyA8LSBnZXRNYXJrZXJDbGFzc2VzKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCwgIm5ld19tYXJrZXJzIikKCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJuZXdfbWFya2VycyIsIHJlX29yZGVyX21hcmtlcnM9VFJVRSwgbWFya2VyX2xldmVscz1uZXdfbWFya2Vyc19sZXZlbHMsCiAgICAgICAgbV9jb2xvdXJzPWdldENsYXNzQ29sb3VycygpW2MoMTo1LCA3LCAxMDoyMCldLCBmb2k9bnVjbGVhcl9wb3JlX2NoYW5uZWwpCnByaW50KHApCgoKcCA8LSBwbG90UENBKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCwgIm5ld19tYXJrZXJzIiwgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW5ld19tYXJrZXJzX2xldmVscywKICAgICAgICBtX2NvbG91cnM9Z2V0Q2xhc3NDb2xvdXJzKClbYygxOjUsIDcsIDEwOjIwKV0sIGZvaT1udWNsZWFyX3BvcmVfYmFza2V0KQpwcmludChwKQoKYGBgCgoKYGBge3J9CmdldE1hcmtlckNsYXNzZXMocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwLCAibmV3X21hcmtlcnMiKQoKc2V0LnNlZWQoMSkKcHJvaiA8LSBtYWtlX3Byb2ooInQtU05FIiwgcHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwLCAibmV3X21hcmtlcnMiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KEhtaXNjKQpgYGAKCmBgYHtyfQpwcm9qX2RmIDwtIHByb2okUENBX2RmCm1hcmtlcl9sZXZlbHMgPC0gc2V0ZGlmZihuZXdfbWFya2Vyc19sZXZlbHMsICJ1bmtub3duIikKbWFya2VyX2xldmVscyA8LSBtYXJrZXJfbGV2ZWxzW2MoMiwzLDQsNiw4LDEsNSw3KV0KI21fY29sb3VycyA8LSBnZXRTdG9ja2NvbCgpW2MoMSw3LDQsMywyLDUpXQptX2NvbG91cnMgPC0gYyhjYlBhbGV0dGVbYyg2LDMsMiw0LDgsNyw1KV0sICJncmV5MjAiKQoKcHJval9kZiRtYXJrZXJzIDwtIGZhY3Rvcihwcm9qX2RmJG5ld19tYXJrZXJzLCBsZXZlbHM9bWFya2VyX2xldmVscykKcHJpbnQodGFibGUoaXMubmEocHJval9kZiRtYXJrZXJzKSkpCnByb2pfZGYkdW5rbm93biA8LSBwcm9qX2RmJG5ld19tYXJrZXJzPT0idW5rbm93biIKCnAgPC0gZ2dwbG90KHByb2pfZGYsIGFlcyhYLCBZLCBjb2xvdXI9bWFya2VycykpICsKICAgIGdlb21fcG9pbnQoYWVzKFgsIFksIGNvbG91cj1tYXJrZXJzLCBhbHBoYT11bmtub3duKSwgc2l6ZT0yKSArCiAgICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWMoMSwwLjIpLCBndWlkZT1GKSArCiAgICBteV90aGVtZSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1tX2NvbG91cnMsIG5hLnZhbHVlPSJncmV5NjAiLCBuYW1lPSIiLCBicmVha3M9YyhtYXJrZXJfbGV2ZWxzKSkgKwogICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGFscGhhID0gMSwgc2l6ZT0yKSkpICsKICAgIHhsYWIoIkRpbWVuc2lvbiAxIikgKyB5bGFiKCJEaW1lbnNpb24gMiIpCgpwcmludChwKQpnZ3NhdmUoIi4vT3V0cHV0L3RTTkVfUkJQX21hcmtlcnMucG5nIikKd3JpdGUudGFibGUocHJval9kZiwgIi4vT3V0cHV0L3RTTkVfcHJvamVjdGlvbnMudHN2Iiwgc2VwPSJcdCIsIHF1b3RlPUZBTFNFLCByb3cubmFtZT1GQUxTRSkKCgpgYGAKYGBge3J9CnAgPC0gUGxvdE1hcmtlclByb2ZpbGVzKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCwgIm5ld19tYXJrZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAga2VlcF9tYXJrZXJzPWdldE1hcmtlckNsYXNzZXMocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwLCAibmV3X21hcmtlcnMiKSwgb3JnYW5lbGxlX29yZGVyPW5ld19tYXJrZXJzX2xldmVscykgKwogIGZhY2V0X3dyYXAofm1hcmtlcnMpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1nZXRDbGFzc0NvbG91cnMoKVtjKDE6NSwgNywgMTA6MjApXSwgZ3VpZGU9RkFMU0UpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwcmludChwKQpgYGAKYGBge3J9Cgpwcm90ZWluX2luZm8gPC0gcmVhZC5kZWxpbSgiSW5wdXQvQWdncmVnYXRlZC1wcm90ZWlucy0yMTg3LXdpdGgtdW5pcHJvdC50YWIiKSAlPiUKICBkcGx5cjo6c2VsZWN0KEVudHJ5LCBnZW5lX25hbWU9R2VuZS5uYW1lcy4uLnByaW1hcnkuLikKCnRvdGFsLnByb3QgPSByZWFkUkRTKCIuL0lucHV0L3Byb3RfcmVzXzIwX2ZyYWN0aW9uc19pbXB1dGVkX21hcmtlcnMucmRzIikKY29sbmFtZXModG90YWwucHJvdClbMTJdIDwtICIwLjk0OCIKY29sbmFtZXModG90YWwucHJvdCkgPC0gYyhzZXEoMSwyMCwyKSwgc2VxKDIsMjAsMikpCnRvdGFsLnByb3QgPC0gdG90YWwucHJvdFssb3JkZXIoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoY29sbmFtZXModG90YWwucHJvdCkpKSldCnRvdGFsLnByb3QgPC0gdG90YWwucHJvdFssNToxOV0KdG90YWwucHJvdCA9IG5vcm1hbGlzZSh0b3RhbC5wcm90LCJzdW0iKQoKcmJwLnByb3QgPC0gcHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwCmNvbG5hbWVzKHJicC5wcm90KSA8LSA1OjE5CgpwcmludChkaW0odG90YWwucHJvdCkpCnByaW50KGRpbShyYnAucHJvdCkpCgpwcmludChjb2xuYW1lcyh0b3RhbC5wcm90KSkKcHJpbnQoY29sbmFtZXMocmJwLnByb3QpKQoKYGBgCgoKCmBgYHtyfQpwbG90Q29tYmluZWRQcm9maWxlcyA8LSBmdW5jdGlvbihmb2kpewogIAogIGZvaV9pbl90b3RhbCA8LSBpbnRlcnNlY3QoZm9pLCByb3duYW1lcyh0b3RhbC5wcm90KSkKICBmb2lfaW5fcmJwIDwtIGludGVyc2VjdChmb2ksIHJvd25hbWVzKHJicC5wcm90KSkKICAKICBmb2lfaW5fYm90aCA8LSBpbnRlcnNlY3QoZm9pX2luX3JicCwgZm9pX2luX3RvdGFsKQogIAogIHByaW50KGZvaV9pbl9ib3RoKQogIGlmKGxlbmd0aChmb2lfaW5fYm90aCk9PTApewogICAgcmV0dXJuKE5BKQogIH0KICAKICB0b3RhbF9leHByc19kZiA8LSBleHBycyh0b3RhbC5wcm90W2ZvaV9pbl9ib3RoLF0pCiAgdG90YWxfZXhwcnNfZGYgPC0gbWVsdCh0b3RhbF9leHByc19kZikKICB0b3RhbF9leHByc19kZiR0eXBlID0gIkFsbCBwcm90ZWluIgogIAogIHJicF9leHByc19kZiA8LSBleHBycyhyYnAucHJvdFtmb2lfaW5fYm90aCxdKQogIHJicF9leHByc19kZiA8LSBtZWx0KHJicF9leHByc19kZikKICByYnBfZXhwcnNfZGYkdHlwZSA9ICJSTkEtYm91bmQiCiAgCiAgI3ByaW50KGhlYWQodG90YWxfZXhwcnNfZGYpKQogICNwcmludChoZWFkKHJicF9leHByc19kZikpCiAgI3ByaW50KGhlYWQocmJpbmQocmJwX2V4cHJzX2RmLCB0b3RhbF9leHByc19kZikpKQogIAogIHAgPC0gcmJpbmQocmJwX2V4cHJzX2RmLCB0b3RhbF9leHByc19kZikgJT4lCiAgICBtZXJnZShwcm90ZWluX2luZm8sIGJ5Lng9IlZhcjEiLCBieS55PSJFbnRyeSIpICU+JQogICAgZ2dwbG90KGFlcyhWYXIyLCB2YWx1ZSwgY29sb3VyPXR5cGUsIGdyb3VwPXR5cGUpKSArCiAgICBteV90aGVtZSArIGdlb21fbGluZSgpICsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbz0xLCBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSwgaGp1c3Q9MSkpICArCiAgICB4bGFiKCJGcmFjdGlvbiIpICsgeWxhYigiU3VtbiBub3JtYWxpc2VkXG5hYnVuZGFuY2UiKSArIAogICAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKG5hbWU9IiIsIG5hLnZhbHVlPSJncmV5IikKICAKICBpZihsZW5ndGgoZm9pX2luX2JvdGgpPjEpewogICAgcCA8LSBwICsgZmFjZXRfd3JhcCh+Z2VuZV9uYW1lKQogIH0KICAKICBwcmludChwKQogIAogIHAyIDwtIHAgPC0gcmJpbmQocmJwX2V4cHJzX2RmLCB0b3RhbF9leHByc19kZikgJT4lCiAgICBtZXJnZShwcm90ZWluX2luZm8sIGJ5Lng9IlZhcjEiLCBieS55PSJFbnRyeSIpICU+JQogICAgZ2dwbG90KGFlcyhWYXIyLCB2YWx1ZSwgY29sb3VyPXR5cGUsIGdyb3VwPWludGVyYWN0aW9uKHR5cGUsIGdlbmVfbmFtZSkpKSArCiAgICBteV90aGVtZSArIGdlb21fbGluZSgpICsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbz0xLCBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSwgaGp1c3Q9MSkpICArCiAgICB4bGFiKCJGcmFjdGlvbiIpICsgeWxhYigiU3VtbiBub3JtYWxpc2VkXG5hYnVuZGFuY2UiKSArIAogICAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKG5hbWU9IiIsIG5hLnZhbHVlPSJncmV5IikKICAKICBwcmludChwMikKICAKICBpbnZpc2libGUobGlzdCgicCI9cCwgInAyIj1wMikpCn0KYGBgCgoKYGBge3J9CnAgPC0gcGxvdENvbWJpbmVkUHJvZmlsZXMocGFyYSkKZ2dzYXZlKCJPdXRwdXQvcGxvdHMvcGFyYXNwZWNrbGUucG5nIiwgcCRwMikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpwbG90Q29tYmluZWRQcm9maWxlcyh0Uk5BX0FBKQoKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpwbG90Q29tYmluZWRQcm9maWxlcyhtb3Rvcl9hY3Rpdml0eSkKYGBgCgpgYGB7cn0KZnZhckxhYmVscyhyYnAucHJvdCkKd2VsbF9xdWFudGlmaWVkX3JicHMgPC0gZkRhdGEocmJwLnByb3QpICU+JQogIGZpbHRlcih6ZXJvX2ltcHV0YXRpb25fbjw9NCwgbWlzc2luZ19uPD0yKSAlPiUKICBwdWxsKG1hc3Rlcl9wcm90ZWluKQoKcGxvdENvbWJpbmVkUHJvZmlsZXMoaW50ZXJzZWN0KG1ybmFfc3BsaWNpbmcsIHdlbGxfcXVhbnRpZmllZF9yYnBzKSkKYGBgCgpgYGB7cn0Kcmlib3NvbWVfcHJvdGVpbnMgPC0gbmFtZXMobWFya2Vyc19wcm90ZWlucylbbWFya2Vyc19wcm90ZWlucz09IlJJQk9TT01FIl0KcGxvdENvbWJpbmVkUHJvZmlsZXMoaW50ZXJzZWN0KHJpYm9zb21lX3Byb3RlaW5zLCB3ZWxsX3F1YW50aWZpZWRfcmJwcykpCmBgYAoKYGBge3J9CnBsb3RDb21iaW5lZFByb2ZpbGVzKCJRMTU5NDIiKQpgYGAKCmBgYHtyfQpwIDwtIHBsb3RDb21iaW5lZFByb2ZpbGVzKCJQMDk4NzQiKQoKZ2dzYXZlKCIuL091dHB1dC9QQVJQMV9wcm9maWxlcy5wbmciLCBwJHAyKQoKYGBgCgo=